package monitor

import (
	"MoSkeleton/config/consts"
	"MoSkeleton/framework"
	fc "MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/dto/response"
	"MoSkeleton/framework/core/motypes"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/monitor"
	"MoSkeleton/services/base"
	monitor2 "MoSkeleton/web/pogos/request/monitor"
	"MoSkeleton/web/pogos/request/public"
	monitor3 "MoSkeleton/web/pogos/response/monitor"
	"bytes"
	"github.com/gin-gonic/gin"
	"github.com/mssola/user_agent"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"strconv"
)

type SysLoginInfoService struct {
	base.BaseService
}

func (s *SysLoginInfoService) AddLoginInfo(c *gin.Context, loginReq *public.LoginReq) {
	loginInfo := new(monitor.SysLogininfor)
	loginInfo.Ipaddr = utils.GetRequestIp(c)
	ua := user_agent.New(c.GetHeader("User-Agent"))
	loginInfo.Os = ua.OS()
	loginInfo.Browser, _ = ua.Browser()
	loginInfo.UserName = loginReq.Username
	loginState, exists := c.Get(fc.GinContext_LoginState_Key)
	if exists && loginState == consts.U_YES {
		loginInfo.Status = consts.U_NORMAL
		loginInfo.Msg = "登录成功"
	} else {
		loginInfo.Status = consts.U_EXCEPTION
		loginInfo.Msg = "登录失败"
	}
	loginInfo.LoginTime = motypes.NowTime()
	go func() {
		result := s.GetDb(nil).Create(loginInfo)
		if result.Error != nil {
			framework.MoLogger.Error("登录日志写入失败", zap.Error(result.Error))
		}
	}()
}

func (s *SysLoginInfoService) GetLoginInfoPage(loginInfoReq *monitor2.SysLoginInforPageReq) (*response.PageResult, error) {
	db := s.getLoginInfoVo(loginInfoReq)
	itemsResp := make([]*monitor3.SysLoginInfoItemResp, 0)
	var total int64
	if err := db.Count(&total).Offset(loginInfoReq.Offset()).Limit(loginInfoReq.Limit()).Find(&itemsResp).Error; err != nil {
		return nil, err
	}
	pageResult := response.NewFromPageInfoAndData(&loginInfoReq.PageInfo, &itemsResp, total)
	return pageResult, nil
}

func (s *SysLoginInfoService) DeleteLoginInfos(ids *[]uint64) (bool, error) {
	result := s.GetDb(nil).Where("id in (?)", *ids).Delete(&monitor.SysLogininfor{})
	return result.RowsAffected > 0, result.Error
}

func (s *SysLoginInfoService) CleanTable() error {
	db := s.GetDb(nil)
	if db.Dialector.Name() == consts.Gorm_DBType_Sqlite3 {
		return db.Exec("DELETE FROM sys_logininfor").Error
	}
	return db.Exec("truncate table sys_logininfor").Error
}

func (s *SysLoginInfoService) ExportLoginInfos(loginInfoReq *monitor2.SysLoginInforPageReq) (*bytes.Buffer, error) {
	db := s.getLoginInfoVo(loginInfoReq)
	itemsResp := make([]*monitor3.ExportLoginInfoItemResp, 0)
	err := db.Find(&itemsResp).Error
	if err != nil {
		return nil, err
	}
	titles := []string{"编号", "用户账号", "登录状态", "登录地址", "浏览器", "操作系统", "提示消息", "访问时间"}
	exportFile := utils.NewExportExcelFile()
	return exportFile.Export(consts.Excel_Sheet_LoginInfo, &titles, func(row, col int, colTitle string) (value any, ok bool, err error) {
		if len(itemsResp) <= row {
			return "", false, nil
		}
		itemResp := itemsResp[row]
		switch col {
		case 0:
			return "'" + strconv.FormatUint(itemResp.ID, 10), true, nil
		case 1:
			return itemResp.UserName, true, nil
		case 2:
			if itemResp.Status == consts.U_NORMAL {
				return "成功", true, nil
			} else {
				return "失败", true, nil
			}
		case 3:
			return itemResp.Ipaddr, true, nil
		case 4:
			return itemResp.Browser, true, nil
		case 5:
			return itemResp.Os, true, nil
		case 6:
			return itemResp.Msg, true, nil
		case 7:
			if itemResp.LoginTime != nil {
				return itemResp.LoginTime.Format("2006-01-02 15:04:05"), true, nil
			} else {
				return "", true, nil
			}
		}
		return "", false, nil
	})
}

func (s *SysLoginInfoService) getLoginInfoVo(loginInfoReq *monitor2.SysLoginInforPageReq) *gorm.DB {
	db := s.GetDb(nil).Select("id, user_name, ipaddr, login_location, browser, os, status, msg, login_time").
		Model(&monitor.SysLogininfor{})
	if loginInfoReq.UserName != "" {
		db.Where("user_name like ?", "%"+loginInfoReq.UserName+"%")
	}
	if loginInfoReq.Ipaddr != "" {
		db.Where("ipaddr like ?", "%"+loginInfoReq.Ipaddr+"%")
	}
	if loginInfoReq.Status != "" {
		db.Where("status = ?", loginInfoReq.Status)
	}
	beginTime, ok := loginInfoReq.GetParamValue("beginTime")
	if ok && beginTime != "" {
		if beginTime = loginInfoReq.FormatBeginTime(beginTime); beginTime != "" {
			db.Where("login_time >= ?", beginTime)
		}
	}
	endTime, ok := loginInfoReq.GetParamValue("endTime")
	if ok && endTime != "" {
		if endTime = loginInfoReq.FormatEndTime(endTime); endTime != "" {
			db.Where("login_time <= ?", endTime)
		}
	}
	db.Order("id desc")
	return db
}
